/**
 * 高性能房间状态管理器
 *
 * 功能说明：
 * 1. 使用内存存储 + localStorage持久化 + 防抖写入
 * 2. 提供O(1)时间复杂度的状态查询和更新
 * 3. 支持批量操作和状态变更订阅
 * 4. 自动防抖保存，避免频繁写入localStorage
 * 5. 页面卸载时强制保存，确保数据不丢失
 *
 * 设计模式：单例模式 + 观察者模式
 * 数据结构：Map<string, Map<string, RoomStatus>> - 房间ID -> 日期 -> 状态
 */

import { RoomStatus } from '../../../types/room';

/**
 * 房间状态更新接口
 * 用于记录状态变更的详细信息
 */
export interface RoomStateUpdate {
  roomId: string; // 房间ID
  date: string; // 日期 (格式: MM/DD)
  status: RoomStatus; // 房间状态
  timestamp?: number; // 更新时间戳
}

/**
 * 状态变更订阅者接口
 * 当房间状态发生变更时，会通知所有订阅者
 */
export interface RoomStateSubscriber {
  (updates: RoomStateUpdate[]): void;
}

/**
 * 房间状态管理器类
 * 采用单例模式，确保全局只有一个状态管理器实例
 */
class RoomStateManager {
  private static instance: RoomStateManager;

  // ========== 核心数据结构 ==========

  /**
   * 内存存储 - 使用嵌套Map提供O(1)查找性能
   * 结构：Map<房间ID, Map<日期, 状态>>
   * 例如：Map("room-1", Map("01/15", "booked"))
   */
  private roomStates: Map<string, Map<string, RoomStatus>> = new Map();

  /**
   * 订阅者列表 - 观察者模式
   * 当状态发生变更时，会通知所有订阅者
   */
  private subscribers: Set<RoomStateSubscriber> = new Set();

  // ========== 防抖保存机制 ==========

  /**
   * 防抖保存定时器
   * 避免频繁写入localStorage，提升性能
   */
  private saveTimer: NodeJS.Timeout | null = null;

  /**
   * 待保存的更新队列
   * 收集所有待保存的状态变更，批量写入localStorage
   */
  private pendingUpdates: RoomStateUpdate[] = [];

  // ========== 配置常量 ==========

  /**
   * localStorage存储键名
   */
  private readonly STORAGE_KEY = 'hotel-room-states';

  /**
   * 防抖延迟时间 - 300ms
   * 在300ms内的多次更新会被合并为一次保存操作
   */
  private readonly DEBOUNCE_DELAY = 300;

  /**
   * 私有构造函数 - 单例模式
   * 初始化时自动加载本地存储的数据，并设置页面卸载时的保存逻辑
   */
  private constructor() {
    // 从localStorage加载已保存的状态数据
    this.loadFromStorage();

    // 页面卸载时强制保存，确保数据不丢失
    if (typeof window !== 'undefined') {
      window.addEventListener('beforeunload', () => {
        this.forceSave();
      });
    }
  }

  /**
   * 获取单例实例
   * 确保整个应用只有一个状态管理器实例
   * @returns RoomStateManager实例
   */
  public static getInstance(): RoomStateManager {
    if (!RoomStateManager.instance) {
      RoomStateManager.instance = new RoomStateManager();
    }
    return RoomStateManager.instance;
  }

  // ========== 核心API方法 ==========

  /**
   * 获取房间状态
   * 时间复杂度：O(1)
   * @param roomId 房间ID
   * @param date 日期 (格式: MM/DD)
   * @returns 房间状态，如果不存在返回null
   */
  public getRoomStatus(roomId: string, date: string): RoomStatus | null {
    const roomMap = this.roomStates.get(roomId);
    if (!roomMap) return null;
    return roomMap.get(date) || null;
  }

  /**
   * 设置房间状态
   * 内部调用批量更新方法，确保状态变更的一致性
   * @param roomId 房间ID
   * @param date 日期 (格式: MM/DD)
   * @param status 新的房间状态
   */
  public setRoomStatus(roomId: string, date: string, status: RoomStatus): void {
    this.batchUpdateStatus([{ roomId, date, status }]);
  }

  /**
   * 批量更新房间状态
   * 这是状态管理的核心方法，负责：
   * 1. 批量处理多个状态更新
   * 2. 只记录真正发生变化的更新
   * 3. 通知所有订阅者
   * 4. 触发防抖保存机制
   *
   * @param updates 状态更新数组
   */
  public batchUpdateStatus(updates: RoomStateUpdate[]): void {
    const processedUpdates: RoomStateUpdate[] = [];
    const timestamp = Date.now();

    // 遍历所有更新请求
    updates.forEach(update => {
      const { roomId, date, status } = update;

      // 确保房间Map存在，避免空指针异常
      if (!this.roomStates.has(roomId)) {
        this.roomStates.set(roomId, new Map());
      }

      const roomMap = this.roomStates.get(roomId)!;

      // 只有状态真正变化才记录，避免无效更新
      const currentStatus = roomMap.get(date);
      if (currentStatus !== status) {
        // 更新内存中的状态
        roomMap.set(date, status);

        // 记录变更信息，用于通知和持久化
        processedUpdates.push({
          roomId,
          date,
          status,
          timestamp,
        });
      }
    });

    // 如果有实际的状态变更
    if (processedUpdates.length > 0) {
      // 添加到待保存队列，等待批量写入localStorage
      this.pendingUpdates.push(...processedUpdates);

      // 通知所有订阅者状态已变更
      this.notifySubscribers(processedUpdates);

      // 触发防抖保存机制
      this.debouncedSave();
    }
  }

  /**
   * 获取房间在指定日期范围内的所有状态
   */
  public getRoomStatesInRange(
    roomId: string,
    startDate: string,
    endDate: string
  ): Map<string, RoomStatus> {
    const roomMap = this.roomStates.get(roomId);
    const result = new Map<string, RoomStatus>();

    if (!roomMap) return result;

    // 生成日期范围
    const dates = this.generateDateRange(startDate, endDate);
    dates.forEach(date => {
      const status = roomMap.get(date);
      if (status) {
        result.set(date, status);
      }
    });

    return result;
  }

  /**
   * 按状态查找房间
   */
  public findRoomsByStatus(status: RoomStatus, date: string): string[] {
    const result: string[] = [];

    this.roomStates.forEach((roomMap, roomId) => {
      if (roomMap.get(date) === status) {
        result.push(roomId);
      }
    });

    return result;
  }

  /**
   * 清除房间状态
   */
  public clearRoomStatus(roomId: string, date?: string): void {
    const roomMap = this.roomStates.get(roomId);
    if (!roomMap) return;

    if (date) {
      // 清除特定日期
      roomMap.delete(date);
      this.notifySubscribers([
        {
          roomId,
          date,
          status: RoomStatus.AVAILABLE, // 假设清除后恢复可用
          timestamp: Date.now(),
        },
      ]);
    } else {
      // 清除所有日期
      roomMap.clear();
    }

    this.debouncedSave();
  }

  /**
   * 订阅状态变更
   */
  public subscribe(callback: RoomStateSubscriber): () => void {
    this.subscribers.add(callback);

    // 返回取消订阅函数
    return () => {
      this.subscribers.delete(callback);
    };
  }

  /**
   * 获取所有状态数据（用于导出）
   */
  public exportAllStates(): Record<string, Record<string, RoomStatus>> {
    const result: Record<string, Record<string, RoomStatus>> = {};

    this.roomStates.forEach((roomMap, roomId) => {
      result[roomId] = {};
      roomMap.forEach((status, date) => {
        result[roomId][date] = status;
      });
    });

    return result;
  }

  /**
   * 导入状态数据
   */
  public importStates(data: Record<string, Record<string, RoomStatus>>): void {
    this.roomStates.clear();

    Object.entries(data).forEach(([roomId, dateStatusMap]) => {
      const roomMap = new Map<string, RoomStatus>();
      Object.entries(dateStatusMap).forEach(([date, status]) => {
        roomMap.set(date, status);
      });
      this.roomStates.set(roomId, roomMap);
    });

    this.forceSave();
  }

  /**
   * 获取状态统计信息
   */
  public getStatistics(date: string): Record<RoomStatus, number> {
    const stats: Record<RoomStatus, number> = {} as Record<RoomStatus, number>;

    // 初始化统计
    Object.values(RoomStatus).forEach(status => {
      stats[status] = 0;
    });

    this.roomStates.forEach(roomMap => {
      const status = roomMap.get(date);
      if (status) {
        stats[status]++;
      }
    });

    return stats;
  }

  // ===== 私有方法 =====

  /**
   * 通知所有订阅者
   */
  private notifySubscribers(updates: RoomStateUpdate[]): void {
    this.subscribers.forEach(callback => {
      try {
        callback(updates);
      } catch (error) {
        console.error('房间状态订阅者回调错误:', error);
      }
    });
  }

  // ========== 防抖保存机制 ==========

  /**
   * 防抖保存
   * 避免频繁写入localStorage，提升性能
   * 如果在300ms内多次调用，只有最后一次会真正执行保存
   */
  private debouncedSave(): void {
    // 清除之前的定时器
    if (this.saveTimer) {
      clearTimeout(this.saveTimer);
    }

    // 设置新的定时器
    this.saveTimer = setTimeout(() => {
      this.forceSave();
    }, this.DEBOUNCE_DELAY);
  }

  /**
   * 立即保存到localStorage
   * 将内存中的所有状态数据序列化并保存到本地存储
   * 在页面卸载时会被强制调用，确保数据不丢失
   */
  private forceSave(): void {
    // 清除防抖定时器
    if (this.saveTimer) {
      clearTimeout(this.saveTimer);
      this.saveTimer = null;
    }

    try {
      // 导出所有状态数据
      const data = this.exportAllStates();

      // 压缩数据（这里使用简单的JSON序列化）
      const compressed = this.compressData(data);

      // 保存到localStorage
      localStorage.setItem(this.STORAGE_KEY, compressed);

      console.log(
        `💾 房间状态已保存，包含 ${this.pendingUpdates.length} 个更新`
      );

      // 清空待保存队列
      this.pendingUpdates = [];
    } catch (error) {
      console.error('保存房间状态失败:', error);
    }
  }

  // ========== 数据持久化方法 ==========

  /**
   * 从localStorage加载数据
   * 在应用启动时自动调用，恢复之前保存的状态
   */
  private loadFromStorage(): void {
    try {
      // 从localStorage读取压缩的数据
      const compressed = localStorage.getItem(this.STORAGE_KEY);
      if (!compressed) return;

      // 解压数据
      const data = this.decompressData(compressed);

      // 导入状态数据到内存
      this.importStates(data);

      console.log('📂 房间状态已从本地存储加载');
    } catch (error) {
      console.error('加载房间状态失败:', error);
    }
  }

  /**
   * 压缩数据
   * 将状态数据序列化为字符串，便于存储
   * 这里使用简单的JSON序列化，实际项目中可以使用更高级的压缩算法
   *
   * @param data 状态数据对象
   * @returns 压缩后的字符串
   */
  private compressData(
    data: Record<string, Record<string, RoomStatus>>
  ): string {
    // 简单的JSON压缩，实际项目中可以使用更高级的压缩算法
    return JSON.stringify(data);
  }

  /**
   * 解压数据
   * 将压缩的字符串反序列化为状态数据对象
   *
   * @param compressed 压缩的字符串
   * @returns 状态数据对象
   */
  private decompressData(
    compressed: string
  ): Record<string, Record<string, RoomStatus>> {
    return JSON.parse(compressed);
  }

  /**
   * 生成日期范围
   */
  private generateDateRange(startDate: string, endDate: string): string[] {
    const dates: string[] = [];
    const start = new Date(startDate);
    const end = new Date(endDate);

    const current = new Date(start);
    while (current <= end) {
      dates.push(current.toISOString().split('T')[0]);
      current.setDate(current.getDate() + 1);
    }

    return dates;
  }
}

// ========== 导出单例实例 ==========

/**
 * 导出单例实例
 * 全局唯一的状态管理器实例，供整个应用使用
 */
export const roomStateManager = RoomStateManager.getInstance();

/**
 * 导出类本身，供类型定义使用
 */
export default RoomStateManager;
